在上一篇文章中,我們已經完成了踩地雷的基礎功能,包括地圖生成、點擊格子與遊戲結束判斷。
今天,我要帶大家實作 重新開始功能
玩家點擊 UI 上的 Restart 按鈕 → 遊戲重新開始。
重新開始後:
var buttonRectRelativePos = image.Rect(0, 0, 32, 32) // 一個方格大小的 button
// drawButtonWithIcon - 繪製 buttonIcon
func (g *GameLayout) drawButtonWithIcon(screen *ebiten.Image, emojiIcon string) {
vector.DrawFilledRect(screen,
float32((ScreenWidth-1.5*gridSize)/2+buttonRectRelativePos.Min.X),
float32(buttonRectRelativePos.Min.Y),
float32(buttonRectRelativePos.Dx()+0.5*gridSize),
float32(buttonRectRelativePos.Dy()+3),
color.RGBA{120, 120, 120, 255},
true,
)
vector.DrawFilledCircle(screen, ScreenWidth/2, gridSize/2, 16,
color.RGBA{180, 180, 0, 255},
true,
)
emojiValue := emojiIcon
emojiXPos := (ScreenWidth) / 2
emojiYPos := PaddingY
emojiOpts := &text.DrawOptions{}
emojiOpts.ColorScale.ScaleWithColor(getTileColor(IsButtonIcon))
emojiOpts.PrimaryAlign = text.AlignCenter
emojiOpts.SecondaryAlign = text.AlignCenter
emojiOpts.GeoM.Translate(float64(emojiXPos), float64(emojiYPos))
text.Draw(screen, emojiValue, &text.GoTextFace{
Source: emojiFaceSource,
Size: 32,
}, emojiOpts)
}
// Restart - 重新建立 Game 狀態
func (g *GameLayout) Restart() {
g.gameInstance = game.NewGame(Rows, Cols, MineCounts)
}
// 偵測 restart icon 有被點擊
if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) {
xPos, yPos := ebiten.CursorPosition()
if xPos >= ((ScreenWidth-1.5*gridSize)/2+buttonRectRelativePos.Min.X) &&
xPos <= (ScreenWidth)/2+buttonRectRelativePos.Max.X+0.5*gridSize &&
yPos >= buttonRectRelativePos.Min.Y &&
yPos <= buttonRectRelativePos.Max.Y+3 {
g.Restart()
}
}
gemini -p "請根據我目前的變動,幫我撰寫出不需要使用ebiten library 的 unit test 來驗證 game restart的遊戲狀態跟遊戲剛開始是一樣的"
我看到您在 internal/layout/layout.go 中新增了一個 Restart 方法,這個方法會透過呼叫 game.NewGame()
來建立一個新的遊戲實例。
為了要驗證遊戲重新開始後的狀態與初始狀態相同,並且不使用 ebiten 函式庫,我將會直接在 internal/game
package 中撰寫一個新的 unit test。
這個測試會模擬以下情境:
1. 建立一個遊戲。
2. 改變遊戲狀態 (例如,揭開一個格子)。
3. 建立一個新的遊戲 (模擬 Restart)。
4. 比較這兩個遊戲的初始狀態是否一致。
func TestGameRestart(t *testing.T) {
const (
rows = 5
cols = 5
mineCount = 5
)
predicableMineShuffler := func(coords []coord) {
// not shuffler
}
newGameWithPredictableMines := func() *Game {
game := NewGame(rows, cols, mineCount)
game.Board.minePositionShuffler = predicableMineShuffler
game.Board.cells = make([][]*Cell, rows)
for r := range game.Board.cells {
game.Board.cells[r] = make([]*Cell, cols)
for c := range game.Board.cells[r] {
game.Board.cells[r][c] = &Cell{}
}
}
game.Board.mineCoords = []coord{}
game.Board.PlaceMines(mineCount)
game.Board.CalculateAdjacentMines()
return game
}
// 1. create an initial game state for comparison
gameInitial := newGameWithPredictableMines()
// 2. create another game to manipulate
gamePlaying := newGameWithPredictableMines()
// 3. Change the state of gamePlaying
// Let's reveal a safe cell (a cell that is not a mine)
// With our predictable shuffler, mines are at (0,0), (0,1), (0,2), (0,3), (0,4)
// Let's reveal cell (4,4) which is safe
gamePlaying.Board.Reveal(4, 4)
assert.NotEqual(t, gameInitial.Board.cells, gamePlaying.Board.cells)
// 5 Simulate a restart by create a new game
restartedGame := NewGame(rows, cols, mineCount)
restartedGame.Board.minePositionShuffler = predicableMineShuffler
restartedGame.Board.minePositionShuffler = predicableMineShuffler
restartedGame.Board.cells = make([][]*Cell, rows)
for r := range restartedGame.Board.cells {
restartedGame.Board.cells[r] = make([]*Cell, cols)
for c := range restartedGame.Board.cells[r] {
restartedGame.Board.cells[r][c] = &Cell{}
}
}
restartedGame.Board.mineCoords = []coord{}
restartedGame.Board.PlaceMines(mineCount)
restartedGame.Board.CalculateAdjacentMines()
// verify that the restarted game state is identical to intial game state
assert.Equal(t, gameInitial.Board.cells, restartedGame.Board.cells)
assert.Equal(t, gameInitial.IsGameOver, restartedGame.IsGameOver)
assert.Equal(t, gameInitial.Board.GetRemainingFlags(), restartedGame.Board.GetRemainingFlags())
}
在這個測試,我透過操作遊戲狀態來驗正,我的 restart 實做
因為把狀態跟畫面呈現分開,因此可以很方便的測試
遊戲結束
點擊icon button 之後
遊戲勝利
點擊icon button 之後
https://github.com/leetcode-golang-classroom/mine-sweeper-game/actions/runs/17359685757
今天我們完成了:
下一步,我會帶大家做 遊戲時間計數器 讓遊戲更完整!